package com.chencq.core.shiro;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.chencq.module.permission.entity.Permission;
import com.chencq.module.permission.service.PermissionService;
import com.chencq.module.user.entity.User;
import com.chencq.module.user.service.UserService;

public class MyRealm extends AuthorizingRealm {
	private static final Logger LOGGER = LoggerFactory
			.getLogger(AuthorizingRealm.class);
	@Autowired
	private UserService userService;
	@Autowired
	private PermissionService permissionService;

	/**
	 * 为当前登录的Subject授予角色和权限
	 * 
	 * @see 经测试:本例中该方法的调用时机为需授权资源被访问时
	 * @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
	 * @see 个人感觉若使用了Spring3
	 *      .1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache
	 * @see 比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		User user = (User) principals.getPrimaryPrincipal();
		if(user != null){
			// 添加session
			Subject currentUser = SecurityUtils.getSubject();
			Session session = currentUser.getSession();
			session.setAttribute("Cuser", user);
			SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
			try {
				// 根据用户名获取权限
				// permissions
				Set<String> permissions = new HashSet<String>();
				List<Permission> permissionsLi = permissionService.findByUserId(user.getUserId());
						
				if (CollectionUtils.isNotEmpty(permissionsLi)) {
					for (Permission foo : permissionsLi) {
						if (StringUtils.isNotEmpty(foo.getCode())) {
							permissions.add(foo.getCode());
						}
					}
				}
				info.addStringPermissions(permissions);
			} catch (Exception e) {
				e.printStackTrace();
				throw new RuntimeException("Fetch roles and permissions for user["
						+user.getUserId()+ "] failed: ", e);
			}

			return info;
		}
		return null;
	}

	/**
	 * 验证当前登录的Subject
	 * 
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		String userName = token.getUsername();
		String password = new String(token.getPassword());
		if (StringUtils.isEmpty(userName)) {
			LOGGER.warn("login username is blank.");
			throw new AccountException("Empty usernames are not allowed by this realm.");
		}
		// 根据用户名加载记录
		User user;
		try {
			user = userService.getUserByName(userName);
		} catch (Exception e) {
			e.printStackTrace();
			throw new AuthenticationException("cannot query user[" + userName+ "]", e);
		}
		if (user == null) {
			final String errmsg = "No account found for user [" + userName+ "]";
			LOGGER.warn(errmsg);
			throw new UnknownAccountException(errmsg);
		}
		if (user.getStatus() == 2) {// 锁定状态
			throw new LockedAccountException("user [" + userName+ "] disabled.");
				
		}
		if(!user.getPassWord().equals(password)){
			LOGGER.warn("login password is error.");
			throw new IncorrectCredentialsException();
		}
		
		return new SimpleAuthenticationInfo(user,user.getPassWord(), getName());
	}
}
